//
// Created by mio on 25-4-20.
//

#ifndef AMF_H
#define AMF_H

#include <string>
#include <unordered_map>
#include <cstdint>
#include <memory>

typedef enum
{
	AMF0_NUMBER = 0,
	AMF0_BOOLEAN,
	AMF0_STRING,
	AMF0_OBJECT,
	AMF0_MOVIECLIP,		/* reserved, not used */
	AMF0_NULL,
	AMF0_UNDEFINED,
	AMF0_REFERENCE,
	AMF0_ECMA_ARRAY,
	AMF0_OBJECT_END,
	AMF0_STRICT_ARRAY,
	AMF0_DATE,
	AMF0_LONG_STRING,
	AMF0_UNSUPPORTED,
	AMF0_RECORDSET,		/* reserved, not used */
	AMF0_XML_DOC,
	AMF0_TYPED_OBJECT,
	AMF0_AVMPLUS,		/* switch to AMF3 */
	AMF0_INVALID = 0xff
} AMF0DataType;

typedef enum
{
	AMF_NUMBER,
	AMF_BOOLEAN,
	AMF_STRING,
} AmfObjectType;

/**
 * @struct AmfObject
 * @brief 用于存储单个解码后的 AMF0 值的容器结构体。
 * 类似于一个简单的 variant，可以存储 Number, Boolean 或 String。
 */
struct AmfObject
{
	AmfObjectType type;

	std::string amf_string;
	double amf_number;
	bool amf_boolean;

	AmfObject()
	{

	}

	AmfObject(std::string str)
	{
		this->type = AMF_STRING;
		this->amf_string = str;
	}

	AmfObject(double number)
	{
		this->type = AMF_NUMBER;
		this->amf_number = number;
	}
};


/**
 * @typedef AmfObjects
 * @brief 定义 AMF 对象或 ECMA 数组的类型别名。
 * 使用无序映射 (哈希表) 存储键值对，键是字符串，值是 AmfObject。
 */
typedef std::unordered_map<std::string, AmfObject> AmfObjects;


/**
 * @class AmfDecoder
 * @brief 实现 AMF0 数据解码的类。
 * 可以从原始字节流中解析出 AMF0 编码的数据。
 */
class AmfDecoder
{
public:
	/* n: 解码次数 */
	/**
	 * @brief 解码 AMF0 数据。
	 * @param data 指向待解码数据的指针。
	 * @param size 待解码数据的字节数。
	 * @param n 要解码的 AMF0 数据项的数量。-1 表示解码所有可用的数据项。
	 * @return 成功解码并消耗的字节数；如果数据不足或出错可能返回小于预期消耗的字节数或0。
	 */
    int decode(const char *data, int size, int n=-1);

    void reset()
    {
        m_obj.amf_string = "";
        m_obj.amf_number = 0;
        m_objs.clear();
    }

    std::string getString() const
    { return m_obj.amf_string; }

    double getNumber() const
    { return m_obj.amf_number; }

	/**
	 * @brief 检查解码出的对象集合中是否包含指定的键。
	 * @param key 要查找的键。
	 * @return bool 如果键存在则返回 true，否则返回 false。
	 */
    bool hasObject(std::string key) const
    { return (m_objs.find(key) != m_objs.end()); }

    AmfObject getObject(std::string key)
    { return m_objs[key]; }

    AmfObject getObject()
    { return m_obj; }

    AmfObjects getObjects()
    { return m_objs; }

private:
	/**
	 * @brief 解码 AMF0 Boolean。
	 * @param data 指向布尔值字节 (0x00 或 0x01)。
	 * @param size 剩余数据大小。
	 * @param amf_boolean 输出参数，存储解码结果。
	 * @return int 消耗的字节数 (1)。如果 size 不足返回 0。
	 */
    static int decodeBoolean(const char *data, int size, bool& amf_boolean);
	/**
	 * @brief 解码 AMF0 Number (8 字节大端序 double)。
	 * @param data 指向 8 字节 double 数据。
	 * @param size 剩余数据大小。
	 * @param amf_number 输出参数，存储解码结果。
	 * @return int 消耗的字节数 (8)。如果 size 不足返回 0。
	 */
    static int decodeNumber(const char *data, int size, double& amf_number);
	/**
	 * @brief 解码 AMF0 String (2 字节大端序长度 + UTF-8 内容)。
	 * @param data 指向 2 字节长度字段。
	 * @param size 剩余数据大小。
	 * @param amf_string 输出参数，存储解码结果。
	 * @return int 消耗的总字节数 (2 + len)。如果 size 不足返回 0 或 -1。
	 */
    static int decodeString(const char *data, int size, std::string& amf_string);
    static int decodeObject(const char *data, int size, AmfObjects& amf_objs);
    static uint16_t decodeInt16(const char *data, int size);
    static uint32_t decodeInt24(const char *data, int size);
    static uint32_t decodeInt32(const char *data, int size);

    AmfObject m_obj;		/// 用于存储最后解码的单个 AMF 值
    AmfObjects m_objs;		/// 用于存储解码出的 AMF Object 或 ECMA Array 的键值对
};


/**
 * @class AmfEncoder
 * @brief 实现 AMF0 数据编码的类。
 * 可以将程序中的数据结构编码成 AMF0 格式的字节流。
 */
class AmfEncoder
{
public:
	/**
	 * @brief 构造函数。
	 * @param size 初始分配的内部缓冲区大小。
	 */
	AmfEncoder(uint32_t size = 1024);
	virtual ~AmfEncoder();

	void reset()
	{
		m_index = 0;
	}

	std::shared_ptr<char> data()
	{
		return m_data;
	}

	uint32_t size() const
	{
		return m_index;
	}

	/**
	 * @brief 编码 AMF0 String 或 Long String。
	 * @param str 要编码的 C 字符串。
	 * @param len 字符串长度。
	 * @param isObject 如果为 true，则写入类型标记 (AMF0_STRING 或 AMF0_LONG_STRING)。
	 *                 如果为 false，则不写类型标记（用于 Object 的 Key）。
	 */
	void encodeString(const char* str, int len, bool isObject=true);
	void encodeNumber(double value);
	void encodeBoolean(int value);
	/**
	 * @brief 编码 AMF0 Object。
	 * 写入 Object 类型标记，然后遍历 Map，编码每个 Key(String, 无类型标记) 和 Value(Any AMF Type)。
	 * 最后写入 Object End Marker (空字符串 Key + AMF0_OBJECT_END 标记)。
	 * @param objs 要编码的键值对 Map。
	 */
	void encodeObjects(AmfObjects& objs);
	/**
	 * @brief 编码 AMF0 ECMA Array。
	 * 写入 ECMA Array 类型标记，写入 4 字节的 count (当前固定为0)，然后类似 Object 编码键值对，
	 * 最后写入 Object End Marker。
	 * @param objs 要编码的键值对 Map。
	 */
	void encodeECMA(AmfObjects& objs);

private:
	void encodeInt8(int8_t value);
	void encodeInt16(int16_t value);
	void encodeInt24(int32_t value);
	void encodeInt32(int32_t value);
	void realloc(uint32_t size);

	std::shared_ptr<char> m_data;	/// 使用智能指针管理的内部数据缓冲区
	uint32_t m_size  = 0;			/// 当前分配的缓冲区总大小
	uint32_t m_index = 0;			/// 当前写入位置（即已编码数据的长度）
};


#endif //AMF_H
